package cn.texous.demo.dgs.filter;

import cn.texous.util.commons.constant.ResultCode;
import cn.texous.util.commons.constant.Result;
import cn.texous.util.commons.util.GsonUtils;
import cn.texous.util.commons.util.StringUtils;
import cn.texous.demo.dgs.client.AuthorClient;
import cn.texous.demo.dgs.util.PathUtils;
import cn.texous.demo.dgs.util.ServerHttpRequestUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.charset.StandardCharsets;

/**
 * insert description here
 *
 * @author leven
 * @since 2019/7/13 19:11
 */
@Slf4j
@Component
public class MyAuthorizationFilter implements GlobalFilter, Ordered {

    private static final ObjectMapper OM = new ObjectMapper();

    @Autowired
    private AuthorClient authorClient;

    @Override
    public int getOrder() {
        return 0;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        URI uri = request.getURI();
        GsonUtils.prettyInfoPrint(String.format("接收到请求：%s", uri.getPath()));
        // 判断是否为登录操作
        if (isLoginUri(uri)) {
            // 登陆
            Mono<ClientResponse> data = authorClient.login(request);
            return completeFilter(exchange.getResponse(), data);
        }
        // 判断是否是不需要鉴权的路径
        if (isNotAuthUri(uri)) {
            return chain.filter(exchange);
        }

        if (isCheckTokenUri(uri)) {
            // 判断access_token 是否有效
            boolean effective = effectiveAccessToken(request);
            return completeFilter(exchange.getResponse(), Result.ok(effective));
        }

        // 发起路由鉴权
        Result data = authorClient.authUri(request);
        // 鉴权成功则继续访问
        if (data != null && ResultCode.SUCCESS.getCode() == data.getCode())
            return chain.filter(exchange);

        return completeFilter(exchange.getResponse(), data);
    }

    private boolean effectiveAccessToken(ServerHttpRequest request) {
        String accessToken = ServerHttpRequestUtils.getAccessToken(request);
        if (StringUtils.isTrimEmpty(accessToken))
            return false;
        return authorClient.effectiveAccessToken(accessToken);
    }

    private boolean isCheckTokenUri(URI uri) {
        return PathUtils.isCheckTokenUri(uri.getPath());
    }

    private boolean isLoginUri(URI uri) {
        return PathUtils.isLoginUri(uri.getPath());
    }

    private boolean isNotAuthUri(URI uri) {
        return PathUtils.isNotAuthUri(uri.getPath());
    }

    private Mono<Void> completeFilter(ServerHttpResponse response, Object data) {
        if (data != null) {
            response.getHeaders().add("Content-Type",
                    MediaType.APPLICATION_JSON_UTF8_VALUE);
            byte[] bytes = getDataAsBytes(data);
            DataBuffer buffer = response.bufferFactory().wrap(bytes);
            return response.writeWith(Flux.just(buffer));
        }
        return response.setComplete();
    }

    private byte[] getDataAsBytes(Object data) {
        try {
            return OM.writeValueAsBytes(data);
        } catch (JsonProcessingException e) {
            log.error("转换 Object - Bytes 失败", e);
        }
        return GsonUtils.toJson(Result.out(ResultCode.FAIL))
                .getBytes(StandardCharsets.UTF_8);
    }

}
